package com.aiatomic.couting.widgets;

import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.aiatomic.couting.IConstant;
import com.aiatomic.couting.ScanActivity;
import com.aiatomic.couting.scan.ScanDrawable;
import com.aiatomic.couting.util.DisplayUtil;

import android.animation.ValueAnimator;
import android.animation.ValueAnimator.AnimatorUpdateListener;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Paint.FontMetricsInt;
import android.graphics.PointF;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.preference.PreferenceManager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.animation.DecelerateInterpolator;
import android.widget.ImageView;

public class TaggableImageView extends ImageView implements AnimatorUpdateListener, OnClickListener {
	private TaggableSizeChangeListener sizeChangeListener = null;
	private int MODE_ZOOM = 1, MODE_MOVE = 2;
	private int mode = 0;
	private Matrix matrix = new Matrix();
	private PointF last = new PointF();
	private PointF midXY = new PointF();
	private int viewWidth, viewHeight;
	private float last_dist = 0;
	private float max_scale = 5.0f, min_scale = 0.75f;
	private float normalizedScale = 1.0f;
	private float scale_min_detla = 0.01f;
	private ScanActivity activity;
	// 识别结果
	public List<ScanDrawable> drawables = new ArrayList<ScanDrawable>();
	public int sensitive_min = 0;
	public float tag_size_scale = 1.0f;
	public boolean show_num = true;
	private SharedPreferences shared;
	private int[] border_colors;
	private long random = 0;

	public enum TAG_STAGE {
		ADD, DEL, NORMAL
	};// 添加标签，删除标签，无操作状态

	public TAG_STAGE tag_state = TAG_STAGE.NORMAL;
	private Paint mPaint = new Paint();
	private int touchedIdx = -1;

	public int reCount() {
		int count = 0;
		for (ScanDrawable scan : drawables) {
			if (scan.sensitive < sensitive_min) {
				continue;
			}
			count++;
		}
		activity.txtCount.setText(String.valueOf(count));
		return count;
	}

	public void reScan() {
		this.tag_size_scale = shared.getFloat("tag_size_scale", 1.0f);
		this.border_colors = new int[] { shared.getInt(IConstant.COLOR1, Color.RED),
				shared.getInt(IConstant.COLOR2, Color.YELLOW), shared.getInt(IConstant.COLOR3, Color.GREEN),
				shared.getInt(IConstant.COLOR4, Color.BLUE) };
		drawables.clear();
		reCount();
		invalidate();
	}

	private void init() {
		activity = (ScanActivity) super.getContext();
		random = Calendar.getInstance().getTimeInMillis();
		shared = activity.getSharedPreferences("setting", activity.MODE_PRIVATE);
		border_colors = new int[] { shared.getInt(IConstant.COLOR1, Color.RED),
				shared.getInt(IConstant.COLOR2, Color.YELLOW), shared.getInt(IConstant.COLOR3, Color.GREEN),
				shared.getInt(IConstant.COLOR4, Color.BLUE) };
		this.setOnClickListener(this);
	}

	public TaggableImageView(Context paramContext) {
		super(paramContext);
		init();
	}

	public TaggableImageView(Context paramContext, AttributeSet paramAttributeSet) {
		super(paramContext, paramAttributeSet);
		init();
	}

	public TaggableImageView(Context paramContext, AttributeSet paramAttributeSet, int paramInt) {
		super(paramContext, paramAttributeSet, paramInt);
		init();
	}

	protected void onSizeChanged(int paramInt1, int paramInt2, int paramInt3, int paramInt4) {
		super.onSizeChanged(paramInt1, paramInt2, paramInt3, paramInt4);
		if (this.sizeChangeListener != null)
			this.sizeChangeListener.sizeChanged();
	}

	public void setOnSizeChangedListener(TaggableSizeChangeListener parama) {
		this.sizeChangeListener = parama;
	}

	public static abstract interface TaggableSizeChangeListener {
		public abstract void sizeChanged();
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		Drawable drawable = getDrawable();
		if (drawable == null || drawable.getIntrinsicWidth() == 0 || drawable.getIntrinsicHeight() == 0) {
			setMeasuredDimension(0, 0);
			return;
		}

		int drawableWidth = drawable.getIntrinsicWidth();
		int drawableHeight = drawable.getIntrinsicHeight();
		int widthSize = MeasureSpec.getSize(widthMeasureSpec);
		int widthMode = MeasureSpec.getMode(widthMeasureSpec);
		int heightSize = MeasureSpec.getSize(heightMeasureSpec);
		int heightMode = MeasureSpec.getMode(heightMeasureSpec);
		viewWidth = setViewSize(widthMode, widthSize, drawableWidth);
		viewHeight = setViewSize(heightMode, heightSize, drawableHeight);

		//
		// Set view dimensions
		//
		setMeasuredDimension(viewWidth, viewHeight);

		//
		// Fit content within view
		//
		fitImageToView();
	}

	@Override
	public void setImageMatrix(Matrix matrix) {
		super.setImageMatrix(matrix);
	}

	private void fitImageToView() {
		Drawable drawable = getDrawable();
		if (drawable == null || drawable.getIntrinsicWidth() == 0 || drawable.getIntrinsicHeight() == 0) {
			return;
		}
		if (matrix == null) {
			return;
		}

		int drawableWidth = drawable.getIntrinsicWidth();
		int drawableHeight = drawable.getIntrinsicHeight();

		//
		// Scale image for view
		//
		float scaleX = (float) viewWidth / drawableWidth;
		float scaleY = (float) viewHeight / drawableHeight;
		scaleX = scaleY = Math.min(scaleX, scaleY);
		float redundantXSpace = viewWidth - (scaleX * drawableWidth);
		float redundantYSpace = viewHeight - (scaleY * drawableHeight);
		matrix.setScale(scaleX, scaleY);
		matrix.postTranslate(redundantXSpace / 2, redundantYSpace / 2);
		setImageMatrix(matrix);
		normalizedScale = 1.0f;
	}

	private int setViewSize(int mode, int size, int drawableWidth) {
		int viewSize;
		switch (mode) {
		case MeasureSpec.EXACTLY:
			viewSize = size;
			break;

		case MeasureSpec.AT_MOST:
			viewSize = Math.min(drawableWidth, size);
			break;

		case MeasureSpec.UNSPECIFIED:
			viewSize = drawableWidth;
			break;

		default:
			viewSize = size;
			break;
		}
		return viewSize;
	}

	private void savePreviousImageValues() {
		setScaleType(ScaleType.MATRIX);
		matrix.set(getImageMatrix());
		fitImageToView();
	}

	@Override
	public void setImageURI(Uri uri) {
		super.setImageURI(uri);
		savePreviousImageValues();
	}

	@Override
	public void setImageDrawable(Drawable drawable) {
		super.setImageDrawable(drawable);
		savePreviousImageValues();
	}

	@Override
	public void setImageBitmap(Bitmap bm) {
		super.setImageBitmap(bm);
		savePreviousImageValues();
	}

	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		int saveCount = canvas.getSaveCount();
		canvas.save();
		canvas.concat(matrix);
		int i = 1;
		for (ScanDrawable scan : drawables) {
			if (scan.sensitive < sensitive_min) {
				continue;
			}
			mPaint.setColor(Color.GRAY);
			mPaint.setStyle(Paint.Style.FILL);
			if (scan.touched) {
				mPaint.setColor(Color.BLUE);
			}
//			mPaint.setAlpha(100);
			mPaint.setAlpha(255);
//			canvas.drawCircle(scan.x, scan.y, (scan.width * tag_size_scale) / 2, mPaint);
			canvas.drawCircle(scan.x, scan.y, (16 * tag_size_scale) / 2, mPaint);
//			mPaint.setAlpha(150);
			mPaint.setAlpha(255);
			if (scan.isUserAdded) {
				mPaint.setColor(border_colors[3]);
			} else {
				mPaint.setColor(border_colors[Math.abs(scan.sensitive) % 3]);
			}
			mPaint.setStyle(Paint.Style.STROKE);
			mPaint.setStrokeWidth(0.5f);
			mPaint.setAntiAlias(true);// 空心效果
//			canvas.drawCircle(scan.x, scan.y, (scan.width * tag_size_scale) / 2, mPaint);
			canvas.drawCircle(scan.x, scan.y, (16 * tag_size_scale) / 2, mPaint);
			mPaint.setStyle(Paint.Style.FILL);
			if (show_num) {
				if (scan.isUserAdded) {
					mPaint.setColor(border_colors[3]);
				} else {
					mPaint.setColor(border_colors[Math.abs(scan.sensitive) % 3]);
				}
				mPaint.setTextAlign(Paint.Align.CENTER);
//				mPaint.setAlpha(150);
				mPaint.setAlpha(255);
//				mPaint.setTextSize((int) (scan.width * 0.8 * tag_size_scale));
				mPaint.setTextSize((int) (3 * tag_size_scale));
				FontMetricsInt fontMetrics = mPaint.getFontMetricsInt();
				float baseline = (mPaint.ascent() + mPaint.descent()) / 2f;
//				int baseline = (fontMetrics.bottom + fontMetrics.top) / 2;
				canvas.drawText(String.valueOf(i), scan.x, scan.y - baseline, mPaint);
			}
			i++;
		}
		canvas.restoreToCount(saveCount);
	}

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		int mAction = event.getActionMasked();
		switch (mAction) {
		case MotionEvent.ACTION_DOWN:
			if (tag_state == TAG_STAGE.NORMAL) {
				float[] dst = getPointerCoords(this, event);
				for (int i = 0; i < drawables.size(); i++) {
					if (drawables.get(i).getBound().contains(dst[0], dst[1])) {
						touchedIdx = i;
					}
				}
				if (touchedIdx >= 0) {
					drawables.get(touchedIdx).touched = true;
					invalidate();
				}
			}
			mode = MODE_MOVE;
			last.set(event.getX(), event.getY());
			break;
		case MotionEvent.ACTION_POINTER_DOWN:
			mode = MODE_ZOOM;
			last_dist = spacing(event);
			midXY = getPointerMidByEvent(event);
			break;
		case MotionEvent.ACTION_MOVE:
			if (mode == MODE_MOVE) {
				if (touchedIdx >= 0) {
					/*
					 * float[] last_dst=getPointerCoords(this,event); float []
					 * lastImgPoint=getPointerCoords(this,last);
					 */
					float deltaX = event.getX() - last.x;
					float deltaY = event.getY() - last.y;
					float[] values = new float[9];
					matrix.getValues(values);
					drawables.get(touchedIdx).x += deltaX / values[Matrix.MSCALE_X];
					drawables.get(touchedIdx).y += deltaY / values[Matrix.MSCALE_Y];
					invalidate();
				} else {
					checkMoveTranslate(event);
				}
				last.set(event.getX(), event.getY());
			} else if (mode == MODE_ZOOM) {
				float dist = spacing(event);
				float deltaScale = dist / last_dist;

				if ((normalizedScale * deltaScale) < max_scale && (normalizedScale * deltaScale) > min_scale
						&& Math.abs(1.0f - deltaScale) > scale_min_detla) {

					matrix.postScale(deltaScale, deltaScale, viewWidth / 2, viewHeight / 2);

					fitScaleTranslate(event);
					setImageMatrix(matrix);
					Log.i("DEBUG", String.format("normalizedScale1=%f,normalizedScale2=%f,deltaScale=%f",
							normalizedScale, normalizedScale * deltaScale, deltaScale));
					normalizedScale *= deltaScale;
					last_dist = dist;
				}
			}
			break;
		case MotionEvent.ACTION_UP:
			if (touchedIdx >= 0) {
				drawables.get(touchedIdx).touched = false;
				touchedIdx = -1;
				invalidate();
			}
			if (Math.abs(event.getX() - last.x) < 10 && Math.abs(event.getY() - last.y) < 10) {
				if (tag_state == TAG_STAGE.ADD) {
					int tWidth = 0, tHeight = 0;
					for (ScanDrawable s : drawables) {
						tWidth += s.width;
						tHeight += s.height;
					}
					float[] add_dst = getPointerCoords(this, event);
					ScanDrawable scan = new ScanDrawable();
					scan.isUserAdded = true;
					scan.sensitive = sensitive_min;
					scan.x = (int) add_dst[0];
					scan.y = (int) add_dst[1];
					scan.width = tWidth / drawables.size();
					scan.height = tHeight / drawables.size();
					drawables.add(scan);
					invalidate();
					reCount();
				} else if (tag_state == TAG_STAGE.DEL) {
					ScanDrawable remove = null;
					float[] del_dst = getPointerCoords(this, event);
					for (ScanDrawable s : drawables) {
						if (s.getBound().contains(del_dst[0], del_dst[1])) {
							remove = s;
						}
					}
					if (remove != null) {
						drawables.remove(remove);
						invalidate();
						reCount();
					}
				}
			}
			fitImageToViewAnimator();
			mode = 0;
			break;
		case MotionEvent.ACTION_POINTER_UP:
			mode = 0;
			break;
		}
		Log.i("DEBUG", String.format("action=%d,x=%f,y=%f,current_mode=%d", mAction, event.getX(), event.getY(), mode));
		return true;
	}

	// 转图像坐标系
	final float[] getPointerCoords(ImageView view, PointF point) {
		final float[] coords = new float[] { point.x, point.y };
		Matrix matrix = new Matrix();
		view.getImageMatrix().invert(matrix);
		matrix.postTranslate(view.getScrollX(), view.getScrollY());
		matrix.mapPoints(coords);
		return coords;
	}

	// 转图像坐标系
	final float[] getPointerCoords(ImageView view, MotionEvent e) {
		final int index = e.getActionIndex();
		final float[] coords = new float[] { e.getX(index), e.getY(index) };
		Matrix matrix = new Matrix();
		view.getImageMatrix().invert(matrix);
		matrix.postTranslate(view.getScrollX(), view.getScrollY());
		matrix.mapPoints(coords);
		return coords;
	}

	private boolean checkMoveTranslate(MotionEvent event) {
		float deltaX = event.getX() - last.x;
		float deltaY = event.getY() - last.y;
		if (Math.sqrt(deltaX * deltaX + deltaY * deltaY) < 15) {
			return false;
		}
		RectF rectF = getMatrixRectF();
		if (rectF.width() <= viewWidth && rectF.height() <= viewHeight) {
			return false;
		}
		if (rectF.width() > viewWidth) {
			if ((rectF.left + deltaX) < viewWidth - rectF.width() || (rectF.left + deltaX) > 0) {
				deltaX = 0;
			}
		}
		if (rectF.height() > viewHeight) {
			if ((rectF.top + deltaY) < viewHeight - rectF.height() || (rectF.top + deltaY) > 0) {
				deltaY = 0;
			}
		}
		if (rectF.width() <= viewWidth) {
			deltaX = 0;
		}
		if (rectF.height() <= viewHeight) {
			deltaY = 0;
		}

		matrix.postTranslate(deltaX, deltaY);
		setImageMatrix(matrix);
		return true;
	}

	private boolean fitScaleTranslate(MotionEvent event) {
		float deltaX = 0;
		float deltaY = 0;
		// 绽放要移动到中心
		RectF rectF = getMatrixRectF();
		if (rectF.width() <= viewWidth) {
			deltaX = (viewWidth - rectF.width()) / 2.0f - rectF.left;
		}
		if (rectF.height() <= viewHeight) {
			deltaY = (viewHeight - rectF.height()) / 2.0f - rectF.top;
		}
		if (rectF.width() > viewWidth) {
			if (rectF.left > 0) {
				deltaX = -rectF.left;
			}
			if (rectF.right < viewWidth) {
				deltaX = viewWidth - rectF.right;
			}
		}
		if (rectF.height() >= viewHeight) {
			if (rectF.top > 0) {
				deltaY = -rectF.top;
			}
			if (rectF.bottom < viewHeight) {
				deltaY = viewHeight - rectF.bottom;
			}
		}
		matrix.postTranslate(deltaX, deltaY);

		return true;
	}

	private void fitImageToViewAnimator() {

		RectF rectF = getMatrixRectF();

		if (rectF.width() < viewWidth && rectF.height() < viewHeight) {
			Drawable drawable = getDrawable();
			if (drawable == null || drawable.getIntrinsicWidth() == 0 || drawable.getIntrinsicHeight() == 0) {
				return;
			}
			if (matrix == null) {
				return;
			}

			int drawableWidth = drawable.getIntrinsicWidth();
			int drawableHeight = drawable.getIntrinsicHeight();

			//
			// Scale image for view
			//
			float scaleX = (float) viewWidth / drawableWidth;
			float scaleY = (float) viewHeight / drawableHeight;
			scaleX = Math.min(scaleX, scaleY);
			float[] values = new float[9];
			matrix.getValues(values);

			ValueAnimator animator = ValueAnimator.ofFloat(Math.min(values[Matrix.MSCALE_X], scaleX),
					Math.max(values[Matrix.MSCALE_X], scaleX));
			animator.addUpdateListener(this);
			animator.setDuration(400);
			animator.setInterpolator(new DecelerateInterpolator());
			animator.setStartDelay(100);
			animator.start();
			normalizedScale = 1.0f;
		}
	}

	/**
	 * 获得图片放大缩小以后的宽和高，以及left，right，top，bottom
	 *
	 * @return
	 */
	private RectF getMatrixRectF() {
		RectF rectF = new RectF();
		Drawable d = getDrawable();
		if (d != null) {
			rectF.set(0, 0, d.getIntrinsicWidth(), d.getIntrinsicHeight());
			matrix.mapRect(rectF);
		}
		return rectF;
	}

	/*
	 * 计算两点之间的中心点
	 * 
	 * @param event
	 * 
	 * @return
	 */
	private PointF getPointerMidByEvent(MotionEvent event) {
		// 得到第一个手指的坐标
		float x1 = event.getX();
		float y1 = event.getY();
		// 得到第二根手指触摸的位置
		float x2 = event.getX(1);
		float y2 = event.getY(1);
		PointF pointF = new PointF();
		pointF.x = (x1 + x2) / 2;
		pointF.y = (y1 + y2) / 2;
		return pointF;
	}

	// 取两点的距离
	private float spacing(MotionEvent event) {
		try {
			float x = event.getX(0) - event.getX(1);
			float y = event.getY(0) - event.getY(1);
			return (float) Math.sqrt(x * x + y * y);
		} catch (IllegalArgumentException ex) {
			ex.printStackTrace();
			return 0;
		}
	}

	@Override
	public void onAnimationUpdate(ValueAnimator animation) {
		Drawable drawable = getDrawable();
		if (drawable == null || drawable.getIntrinsicWidth() == 0 || drawable.getIntrinsicHeight() == 0) {
			setMeasuredDimension(0, 0);
			return;
		}
		int drawableWidth = drawable.getIntrinsicWidth();
		int drawableHeight = drawable.getIntrinsicHeight();

		Float scale = (Float) animation.getAnimatedValue();
		// matrix.reset();
		matrix.setScale(scale, scale);
		float redundantXSpace = viewWidth - (scale * drawableWidth);
		float redundantYSpace = viewHeight - (scale * drawableHeight);
		matrix.postTranslate(redundantXSpace / 2, redundantYSpace / 2);
		setImageMatrix(matrix);
	}

	@Override
	public void onClick(View v) {
		/*
		 * if(tag_state==TAG_STAGE.ADD){
		 * 
		 * }else if(tag_state==TAG_STAGE.DEL){
		 * 
		 * }
		 */
	}

	public Map<String, Object> getScanImage() {
		Map<String, Object> result = new HashMap<String, Object>();
		BitmapDrawable bd = (BitmapDrawable) this.getDrawable();
		Bitmap src = bd.getBitmap().copy(bd.getBitmap().getConfig(), true);
		Canvas canvas = new Canvas(src);
		int i = 1;
		for (ScanDrawable scan : drawables) {
			if (scan.sensitive < sensitive_min) {
				continue;
			}
			mPaint.setColor(Color.GRAY);
			mPaint.setStyle(Paint.Style.FILL);
			if (scan.touched) {
				mPaint.setColor(Color.BLUE);
			}
			mPaint.setAlpha(100);
//			canvas.drawCircle(scan.x, scan.y, (scan.width * tag_size_scale) / 2, mPaint);
			canvas.drawCircle(scan.x, scan.y, (4 * tag_size_scale) / 2, mPaint);
			mPaint.setAlpha(150);

			if (scan.isUserAdded) {
				mPaint.setColor(border_colors[3]);
			} else {
				mPaint.setColor(border_colors[Math.abs(scan.sensitive) % 3]);
			}
			mPaint.setStyle(Paint.Style.STROKE);
//			mPaint.setStrokeWidth(2);
			mPaint.setStrokeWidth(0.5f);
			mPaint.setAntiAlias(true);// 空心效果
//			canvas.drawCircle(scan.x, scan.y, (scan.width * tag_size_scale) / 2, mPaint);
			canvas.drawCircle(scan.x, scan.y, (4 * tag_size_scale) / 2, mPaint);
			mPaint.setStyle(Paint.Style.FILL);
			if (scan.isUserAdded) {
				mPaint.setColor(border_colors[3]);
			} else {
				mPaint.setColor(border_colors[Math.abs(scan.sensitive) % 3]);
			}
			mPaint.setTextAlign(Paint.Align.CENTER);
			mPaint.setAlpha(150);
//			mPaint.setTextSize((int) (scan.width * 0.8 * tag_size_scale));
			mPaint.setTextSize((int) (3 * tag_size_scale));
			FontMetricsInt fontMetrics = mPaint.getFontMetricsInt();
			float baseline = (mPaint.ascent() + mPaint.descent()) / 2f;
//				int baseline = (fontMetrics.bottom + fontMetrics.top) / 2;
			canvas.drawText(String.valueOf(i), scan.x, scan.y - baseline, mPaint);
			i++;
		}

		int fontPxSize = src.getWidth() / 10;
		int padding = fontPxSize / 8;
		Bitmap bg = Bitmap.createBitmap(src.getWidth(), src.getHeight() + (fontPxSize + padding * 2) * 2,
				src.getConfig());
		canvas = new Canvas(bg);
		canvas.drawBitmap(src, 0, fontPxSize + padding * 2, mPaint);
		mPaint.setColor(Color.WHITE);
		mPaint.setTextAlign(Paint.Align.LEFT);
		mPaint.setTextSize(DisplayUtil.px2sp(activity, fontPxSize));
		FontMetricsInt fontMetrics = mPaint.getFontMetricsInt();
		int baseline = (fontMetrics.bottom + fontMetrics.top);
		canvas.drawText("www.paijide.cn", padding, src.getHeight() + fontPxSize + padding * 3 - baseline, mPaint);
		mPaint.setTextAlign(Paint.Align.RIGHT);
		mPaint.setTextSize(DisplayUtil.px2sp(activity, fontPxSize));
		SharedPreferences share = PreferenceManager.getDefaultSharedPreferences(activity);
		String dateFmt = share.getString(IConstant.SAVE_SHOW_DATE, "yyyy/MM/dd E");
		if (dateFmt.equals("None")) {
			dateFmt = "";
		}
		String timeFmt = share.getString(IConstant.SAVE_SHOW_TIME, "H:mm:ss");
		if (timeFmt.equals("None")) {
			timeFmt = "";
		}
		if (!(dateFmt.equals("") && timeFmt.equals(""))) {
			SimpleDateFormat dateFormat = new SimpleDateFormat(dateFmt + timeFmt);
			String date = dateFormat.format(Calendar.getInstance().getTime());
			canvas.drawText(date, src.getWidth() - padding, src.getHeight() + fontPxSize + padding * 3 - baseline,
					mPaint);
			mPaint.setTextAlign(Paint.Align.RIGHT);
			mPaint.setTextSize(DisplayUtil.px2sp(activity, fontPxSize));
		}

		int count = reCount();
		canvas.drawText(String.format("数量:%d", count), src.getWidth() - padding, fontPxSize - padding, mPaint);
		result.put("result", bg);
		result.put("time", random);
		result.put("count", count);
		result.put("apply", bd.getBitmap());
		return result;
	}

}
